#include "gge.h"
#include "SDL_rect.h"

static int LUA_CreateRect(lua_State *L)
{
    int x = (int)luaL_optinteger(L, 1,0);
    int y = (int)luaL_optinteger(L, 2,0);
    int w = (int)luaL_optinteger(L, 3,0);
    int h = (int)luaL_optinteger(L, 4,0);
    SDL_Rect* rect = (SDL_Rect*)lua_newuserdata(L, sizeof (SDL_Rect));
    rect->x = x;
    rect->y = y;
    rect->w = w;
    rect->h = h;
    luaL_setmetatable(L, "SDL_Rect");
    return 1;
}

static int LUA_CreateFRect(lua_State *L)
{
    float x = (float)luaL_optnumber(L, 1,0);
    float y = (float)luaL_optnumber(L, 2,0);
    float w = (float)luaL_optnumber(L, 3,0);
    float h = (float)luaL_optnumber(L, 4,0);
    SDL_FRect* rect = (SDL_FRect*)lua_newuserdata(L, sizeof (SDL_FRect));
    rect->x = x;
    rect->y = y;
    rect->w = w;
    rect->h = h;
    luaL_setmetatable(L, "SDL_FRect");
    return 1;
}

static int LUA_SetRect(lua_State *L)
{
    SDL_Rect * rect = (SDL_Rect*)luaL_checkudata(L, 1, "SDL_Rect");

    rect->x = (int)luaL_optinteger(L,2,rect->x);
    rect->y = (int)luaL_optinteger(L,3,rect->y);
    rect->w = (int)luaL_optinteger(L,4,rect->w);
    rect->h = (int)luaL_optinteger(L,5,rect->h);
    return 0;
}

static int LUA_GetRect(lua_State *L)
{
    SDL_Rect * rect = (SDL_Rect*)luaL_checkudata(L, 1, "SDL_Rect");
    lua_pushinteger(L,rect->x);
    lua_pushinteger(L,rect->y);
    lua_pushinteger(L,rect->w);
    lua_pushinteger(L,rect->h);
    return 4;
}

static int LUA_SetRectXY(lua_State *L)
{
    SDL_Rect * rect = (SDL_Rect*)luaL_checkudata(L, 1, "SDL_Rect");
    rect->x = (int)luaL_optinteger(L,2,rect->x);
    rect->y = (int)luaL_optinteger(L,3,rect->y);
    return 0;
}

static int LUA_GetRectXY(lua_State *L)
{
    SDL_Rect * rect = (SDL_Rect*)luaL_checkudata(L, 1, "SDL_Rect");
    lua_pushinteger(L,rect->x);
    lua_pushinteger(L,rect->y);
    return 2;
}

static int LUA_SetRectWH(lua_State *L)
{
    SDL_Rect * rect = (SDL_Rect*)luaL_checkudata(L, 1, "SDL_Rect");
    rect->w = (int)luaL_optinteger(L,2,rect->w);
    rect->h = (int)luaL_optinteger(L,3,rect->h);
    return 0;
}

static int LUA_GetRectWH(lua_State *L)
{
    SDL_Rect * rect = (SDL_Rect*)luaL_checkudata(L, 1, "SDL_Rect");
    lua_pushinteger(L,rect->w);
    lua_pushinteger(L,rect->h);
    return 2;
}

static int LUA_PointInRect(lua_State *L)
{
    SDL_Rect * rect = (SDL_Rect*)luaL_checkudata(L, 1, "SDL_Rect");
    SDL_Point p;
    p.x = (int)luaL_checkinteger(L,2);
    p.y = (int)luaL_checkinteger(L,3);
    lua_pushboolean(L,SDL_PointInRect(&p, rect));
    return 1;
}

static int LUA_RectEmpty(lua_State *L)
{
    SDL_Rect * rect = (SDL_Rect*)luaL_checkudata(L, 1, "SDL_Rect");
    lua_pushboolean(L,SDL_RectEmpty(rect));
    return 1;
}

static int LUA_RectEquals(lua_State *L)
{
    SDL_Rect * A = (SDL_Rect*)luaL_checkudata(L, 1, "SDL_Rect");
    SDL_Rect * B = (SDL_Rect*)luaL_checkudata(L, 2, "SDL_Rect");
    lua_pushboolean(L,SDL_RectEquals(A, B));
    return 1;
}

static int LUA_HasIntersection(lua_State *L)
{
    SDL_Rect * A = (SDL_Rect*)luaL_checkudata(L, 1, "SDL_Rect");
    SDL_Rect * B = (SDL_Rect*)luaL_checkudata(L, 2, "SDL_Rect");
    lua_pushboolean(L,SDL_HasIntersection(A,B));
    return 1;
}

static int LUA_IntersectRect(lua_State *L)
{
    SDL_Rect * A = (SDL_Rect*)luaL_checkudata(L, 1, "SDL_Rect");
    SDL_Rect * B = (SDL_Rect*)luaL_checkudata(L, 2, "SDL_Rect");

    SDL_Rect *result=(SDL_Rect*)lua_newuserdata(L, sizeof (SDL_Rect));
    luaL_setmetatable(L, "SDL_Rect");
    SDL_IntersectRect(A,B, result);
    return 1;
}

static int LUA_UnionRect(lua_State *L)
{
    SDL_Rect * A = (SDL_Rect*)luaL_checkudata(L, 1, "SDL_Rect");
    SDL_Rect * B = (SDL_Rect*)luaL_checkudata(L, 2, "SDL_Rect");
    SDL_Rect *result = (SDL_Rect*)lua_newuserdata(L, sizeof (SDL_Rect));

    SDL_UnionRect(A, B, result);
    luaL_setmetatable(L, "SDL_Rect");
    return 1;
}
//TODO
//static int LUA_EnclosePoints(lua_State* L)
//{
//    SDL_Rect* A = (SDL_Rect*)luaL_checkudata(L, 1, "SDL_Rect");
//    SDL_Rect* B = (SDL_Rect*)luaL_checkudata(L, 2, "SDL_Rect");
//    SDL_Rect* result = (SDL_Rect*)lua_newuserdata(L, sizeof(SDL_Rect));
//
//    //SDL_EnclosePoints(A, B, result);
//    //luaL_setmetatable(L, "SDL_Rect");
//    return 1;
//}

static int LUA_IntersectRectAndLine(lua_State *L)
{
    SDL_Rect * rect = (SDL_Rect*)luaL_checkudata(L, 1, "SDL_Rect");
    int x1, y1, x2, y2;

    x1 = (int)luaL_checkinteger(L, 2);
    y1 = (int)luaL_checkinteger(L, 3);
    x2 = (int)luaL_checkinteger(L, 4);
    y2 = (int)luaL_checkinteger(L, 5);

    if(SDL_IntersectRectAndLine(rect, &x1, &y1, &x2, &y2)==SDL_TRUE){
        lua_pushinteger(L,x1);
        lua_pushinteger(L,y1);
        lua_pushinteger(L,x2);
        lua_pushinteger(L,y2);
        return 4;
    }
    return 0;
}

static int LUA_RectIndex(lua_State *L){
    SDL_Rect * rect = (SDL_Rect*)luaL_checkudata(L, 1, "SDL_Rect");
    const char* key = lua_tostring(L,2);
    switch(key[0]){
    case 'x':
        lua_pushinteger(L,rect->x);break;
    case 'y':
        lua_pushinteger(L,rect->y);break;
    case 'w':
        lua_pushinteger(L,rect->w);break;
    case 'h':
        lua_pushinteger(L,rect->h);break;
    default:
        luaL_getmetatable(L,"SDL_Rect");
        lua_getfield(L,-1,key);
        lua_remove(L, -2);
    }
    return 1;
}

static int LUA_RectNewIndex(lua_State *L)
{
    SDL_Rect * rect = (SDL_Rect*)luaL_checkudata(L, 1, "SDL_Rect");
    const char* key = lua_tostring(L,2);
    int value = (int)luaL_checkinteger(L,3);

    switch(key[0]){
    case 'x':
        rect->x = value;break;
    case 'y':
        rect->y = value;break;
    case 'w':
        rect->w = value;break;
    case 'h':
        rect->h = value;break;
    }
    return 0;
}

static int LUA_FRectIndex(lua_State *L)
{
    SDL_FRect * rect = (SDL_FRect*)luaL_checkudata(L, 1, "SDL_Rect");
    const char* key = lua_tostring(L,2);
    switch(key[0]){
    case 'x':
        lua_pushnumber(L,rect->x);break;
    case 'y':
        lua_pushnumber(L,rect->y);break;
    case 'w':
        lua_pushnumber(L,rect->w);break;
    case 'h':
        lua_pushnumber(L,rect->h);break;
    default:
        luaL_getmetatable(L,"SDL_Rect");
        lua_getfield(L,-1,key);
        lua_remove(L, -2);
    }
    return 1;
}

static int LUA_FRectNewIndex(lua_State *L)
{
    SDL_FRect * rect = (SDL_FRect*)luaL_checkudata(L, 1, "SDL_Rect");
    const char* key = lua_tostring(L,2);
    float value = (float)luaL_checknumber(L,3);

    switch(key[0]){
    case 'x':
        rect->x = value;break;
    case 'y':
        rect->y = value;break;
    case 'w':
        rect->w = value;break;
    case 'h':
        rect->h = value;break;
    }
    return 0;
}

static const luaL_Reg rect_funcs[] = {
    {"SetRect", LUA_SetRect},
    {"GetRect", LUA_GetRect},
    {"SetRectXY", LUA_SetRectXY},
    {"SetRectWH", LUA_SetRectWH},
    {"GetRectXY", LUA_GetRectXY},
    {"GetRectWH", LUA_GetRectWH},
    
    {"PointInRect", LUA_PointInRect},
    {"RectEmpty", LUA_RectEmpty},
    {"RectEquals", LUA_RectEquals},
    {"HasIntersection", LUA_HasIntersection},
    {"IntersectRect", LUA_IntersectRect},
    {"UnionRect", LUA_UnionRect},
    //{"EnclosePoints", LUA_EnclosePoints},
    {"IntersectRectAndLine", LUA_IntersectRectAndLine},
    
    { NULL, NULL}
};

static const luaL_Reg sdl_funcs[] = {
    {"CreateRect" , LUA_CreateRect} ,
    {"CreateFRect" , LUA_CreateFRect} ,
    { NULL, NULL}
};

int bind_rect(lua_State *L)
{
    luaL_newmetatable(L,"SDL_Rect");
    luaL_setfuncs(L,rect_funcs,0);
    lua_pushcfunction(L,LUA_RectIndex);
    lua_setfield(L, -2, "__index");
    lua_pushcfunction(L,LUA_RectNewIndex);
    lua_setfield(L, -2, "__newindex");
    lua_pop(L, 1);

	luaL_newmetatable(L, "SDL_FRect");
	//luaL_setfuncs(L, frect_funcs, 0);
	lua_pushcfunction(L, LUA_FRectIndex);
	lua_setfield(L, -2, "__index");
	lua_pushcfunction(L, LUA_FRectNewIndex);
	lua_setfield(L, -2, "__newindex");
	lua_pop(L, 1);
    
    luaL_setfuncs(L,sdl_funcs,0);
    return 0;
}